//
//  TicTacOpponent.m
//  TicTacToe
//
//  Created by Tim on 09-11-26.
//  Copyright 2009 Tim Dimsdale. All rights reserved.
//
// This file is part of TicTacToe.
// 
// TicTacToe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// TicTacToe is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with TicTacToe.  If not, see <http://www.gnu.org/licenses/>
//
#import "TicTacOpponent.h"


@implementation TicTacOpponent

// constructor
-(id) init {
	self = [super init];
	if ( self ) {
		skillLevel = DEFAULT_SKILL_LEVEL;
	}
	return self;
}// init

- (void) setPlayerCode: (int) playerCode {
	player = playerCode;
}

-(void) setSkillLevel: (int) skill {
	skillLevel = 1 + skill; // the 1+ is to avoid a /0 problem
	NSLog(@"Current skill level is %d", skillLevel );
}

- (void) setModel: (TicTacModel*) currentModel {
	model = currentModel;
}


- (void) takeTurn: (NSError**)error
{
	// todo, if the game is already won, don't allow moves.

	// TODO: choose by algorithm
	int x,y;
	bool canMove = true;
	
	// TODO: do a random via probability to make these decisions based
	//       on chosen skill level of the computer.
	//       (i.e then let a slider determine skill level of the computer ).
	//       Then do ( if ( random() * skill level >= 90 ) then make Better decision
	//               else if ( random() * skill level >= 70 then make slightly worse decision
	//                   else if ( random() * skill level >= 50 then make random decision
	//                       else ( make desparate decisions ).
	//		pass skill level into each function 
	
	if ( false == [self makeWinningDecisionXOut:&x yOut:&y] ) {
		if ( false == [self makeBlockingDecisionXOut:&x yOut:&y] ) {
			if ( false == [self makeStrategicDecisionXOut:&x yOut:&y] ) {
				if ( false == [self makeRandomDecisionXOut:&x yOut:&y] ) {
					if ( false == [self makeDesperateDecisionXout:&x yOut:&y] ) {
						// this should be a last resort, and at this point
						// we should have already checked to see if anybody won.
						// NSRunAlertPanel(@"Uh oh", @"No move possible", nil, nil, nil);
						NSLog(@"No move possible");
						canMove = false;
					} else
						NSLog(@"Made any decision");
				} else 
					NSLog(@"Made random decision");
			} else
				NSLog(@"Made strategic decision");
		} else 
			NSLog(@"Made blocking decision");	
	} else
		NSLog(@"Made winning decision");
	
	if ( canMove )
		[model setPlayerAtX:x andY:y withPlayer:player error:error];
	else
		*error = [NSError errorWithDomain:@"TicTacModel" code:ERROR_NO_MOVE_POSSIBLE userInfo:nil];

		
}// takeTurn

-(BOOL) makeWinningDecisionXOut: (int*)x yOut:(int*)y  
{
	BOOL foundPosition = FALSE;
	// todo: look for a blocking move.
	// check horizontal
	int otherXA = 0;
	int otherXB = 0;
	int otherYA = 0;
	int otherYB = 0;
	for(int posx=0; posx<3; posx++ ) {
		for(int posy=0; posy<3; posy++ ){
			[self getPositionsOtherThan:posx outOtherA:&otherXA outOtherB:&otherXB ]; // horizontal check
			[self getPositionsOtherThan:posy outOtherA:&otherYA outOtherB:&otherYB ]; // vertical check
			int randomChoice = arc4random()%skillLevel ;
			NSLog(@"Winning decision thought. Random chance is %d, threshold is 50", randomChoice);
			
			if ( [model isPositionAvailableAtX:posx  andY:posy] ) {
				
				if ( ( // first check horizontal  
					( player == [model getPlayerAtX:(otherXA) andY:posy] &&
					 player == [model getPlayerAtX:(otherXB) andY:posy ] ) 
					||
					// then check vertical line
					(	player == [model getPlayerAtX:(posx) andY:otherYA ] &&
						player == [model getPlayerAtX:(posx) andY:otherYB ] )
					) && ( randomChoice > 50 )
					) {
					*x = posx;
					*y = posy;
					foundPosition = true;
				}
			} // if the position is available
		}// for every y
	}// for every x
	
	// TODO: diagonal
	return foundPosition;
}

-(BOOL) makeStrategicDecisionXOut: (int*)x yOut:(int*)y  
{
	BOOL foundPosition = FALSE;
	// todo: look for a blocking move.
	// check horizontal
	int otherXA = 0;
	int otherXB = 0;
	int otherYA = 0;
	int otherYB = 0;
	
	// TODO: logic for "best" positions & strategy
	//  i.e double corners to trap people.
	
	for(int posx=0; posx<3; posx++ ) {
		for(int posy=0; posy<3; posy++ ){
			// TODO: below apply a random % decision to decide whether or not to take the decision based on skill level
			[self getPositionsOtherThan:posx outOtherA:&otherXA outOtherB:&otherXB ]; // horizontal check
			[self getPositionsOtherThan:posy outOtherA:&otherYA outOtherB:&otherYB ]; // vertical check
			
			if ( [model isPositionAvailableAtX:posx  andY:posy] ) {
				int horizCheckA = [model getPlayerAtX:otherXA andY:posy];
				int horizCheckB = [model getPlayerAtX:otherXB andY:posy];
				
				int vertCheckA = [model getPlayerAtX:posx andY:otherYA ];
				int vertCheckB = [model getPlayerAtX:posx andY:otherYB ];		
				
				int randomChoice = arc4random()%skillLevel ;
				NSLog(@"Strategic decision thought. Random chance is %d, threshold is 30", randomChoice);				
				
				if ( ( // first check horizontal  
					(	( player == horizCheckA || TICTAC_NOBODY== horizCheckA) &&
						(player == horizCheckB || TICTAC_NOBODY == horizCheckB) 
					)
					||
					// then check vertical line
					(	( player == vertCheckA || TICTAC_NOBODY == vertCheckA ) &&
						( player == vertCheckB || TICTAC_NOBODY == vertCheckB)
					) 
					) && (  randomChoice > 30)
				) {
					*x = posx;
					*y = posy;
					foundPosition = true;
				}
			} // if the position is available
		}// for every y
	}// for every x
	
	// TODO: diagonal
	return foundPosition;
}



-(BOOL) makeBlockingDecisionXOut: (int*)x yOut:(int*)y  
{
	BOOL foundPosition = FALSE;
	// todo: look for a blocking move.
	// check horizontal
	int otherXA = 0;
	int otherXB = 0;
	int otherYA = 0;
	int otherYB = 0;
	for(int posx=0; posx<3; posx++ ) {
		for(int posy=0; posy<3; posy++ ){
			// TODO: below apply a random % decision to decide whether or not to take the decision based on skill level
			[self getPositionsOtherThan:posx outOtherA:&otherXA outOtherB:&otherXB ]; // horizontal check
			[self getPositionsOtherThan:posy outOtherA:&otherYA outOtherB:&otherYB ]; // vertical check
			
			int randomChoice = arc4random()%skillLevel ;
			NSLog(@"Blocking decision thought. Random chance is %d, threshold is 10", randomChoice);				
						
			if ( [model isPositionAvailableAtX:posx  andY:posy] ) {
				
				if (( // first check horizontal  
					( [model isOtherPlayerAtX:(otherXA) andY:posy andIAm:player] &&
						[model isOtherPlayerAtX:(otherXB) andY:posy andIAm:player] ) 
						||
					// then check vertical line
					(	[model isOtherPlayerAtX:(posx) andY:otherYA andIAm:player] &&
						[model isOtherPlayerAtX:(posx) andY:otherYB andIAm:player] )
					 )  && (  randomChoice > 10)

					) {
					*x = posx;
					*y = posy;
					foundPosition = true;
				}
			} // if the position is available
		}// for every y
	}// for every x
	
	// TODO: diagonal
	return foundPosition;
}

// helper function
-(void) getPositionsOtherThan: (int)inCheck outOtherA:(int*)a outOtherB:(int*)b 
{
	switch (inCheck)
	{
		case 0:
			*a = 1; *b =2;
			break;
		case 1:
			*a = 0; *b = 2;
			break;
		default:
			*a = 1; *b = 0;
			break;
	}
}

- (BOOL) makeRandomDecisionXOut: (int*)x yOut:(int*)y
{
	// as this is completely random, there is really no reason to apply a skill level
	BOOL foundPosition = FALSE;
	int sanity = 1000;

	while ( sanity-- >0 && !foundPosition )
	{
		*x = arc4random()%3;
		*y = arc4random()%3;
		
		if ( [model isPositionAvailableAtX:*x andY:*y ] )  
		{
			foundPosition = TRUE;
		}// if the position is available
	}// while 
	return foundPosition;
}// makeRandomDecisionXOut

-(BOOL) makeDesperateDecisionXout: (int*)x yOut:(int*)y 
{
	BOOL foundPosition = FALSE;
	for( int posx=0; posx<3; posx++ )
	{
		for (int posy=0; posy<3; posy++ ) {
			if ( [model isPositionAvailableAtX:posx andY:posy ] ) 
			{
				foundPosition = TRUE;
				break;
			}
		}
		if ( foundPosition )
			break;
	}
	return foundPosition;
}// makeAnyDecisionXout

@end
